#include "PerformanceCounter.h"

#include <cstdint>
#include <iostream>
#include <iostream>

#include "LowLevel.h"

#if WINDOWS
#include <Windows.h>
#elif LINUX
#include <sys/time.h>
#else
#include <chrono>
#endif


namespace Private
{
	static bool PerformanceCounterSupportQueryPerformance;
	static LARGE_INTEGER PerformanceCounterFrequency;
	static bool PerformanceCounterIsNotInitialized = true;
	static const std::uint64_t PerformanceCounterGLFWFactor(1ULL << 28);
	typedef union
	{
		LARGE_INTEGER ValueLI;
		std::uint64_t ValueU64;
	} PerformanceCounterLargeIntegerCast;
}

PerformanceCounter::PerformanceCounter()
{
	if (Private::PerformanceCounterIsNotInitialized)
	{
#if WINDOWS
		Private::PerformanceCounterSupportQueryPerformance = QueryPerformanceFrequency(&Private::PerformanceCounterFrequency) != 0;
		if (!Private::PerformanceCounterSupportQueryPerformance)
			std::clog << "The QueryPerformanceCounter is not supported!\n";
#endif
		Private::PerformanceCounterIsNotInitialized = false;
	}
}
std::uint64_t PerformanceCounter::GetTicks()
{
#if WINDOWS
	if (Private::PerformanceCounterSupportQueryPerformance)
	{
		Private::PerformanceCounterLargeIntegerCast Conv;
		QueryPerformanceCounter(&(Conv.ValueLI));
		return Conv.ValueU64;
	}
	else
		return GetTickCount();
#elif LINUX
	return 1000000;
#else
	return (std::uint64_t)(std::chrono::high_resolution_clock::now.time_since_epoch().count() * std::chrono::high_resolution_clock::period::den);
#endif
}
std::uint64_t PerformanceCounter::GetFrequency()
{
#if WINDOWS
	if (Private::PerformanceCounterSupportQueryPerformance)
		return Private::PerformanceCounterFrequency.QuadPart;
	else
		return 1000;
#elif LINUX
	timeval Time;
	gettimeofday(&Time, nullptr);
	return static_cast<std::uint64_t>(Time.tv_usec) + static_cast<std::uint64_t>(Time.tv_sec) * 1000000;
#else
	return std::chrono::high_resolution_clock::period::num;
#endif
}
double PerformanceCounter::GetSeconds()
{
	return (static_cast<double>(this->GetTicks())) / (static_cast<double>(this->GetFrequency()));
}